//
//  API.swift
//  GhostWorld
//
//  Created by Kam on 2018/4/2.
//  Copyright © 2018 LKGood. All rights reserved.
//

import Foundation
import Alamofire
import SwiftyJSON

struct UploadData {
  
  enum MimeType: String {
    case image = "image/png"
  }
  
  let data: Data
  let key: String
  let fileName: String
  let mimeType: MimeType
}

class API {
  
  static let shared = API()
  private init() {}
  
  private func getApiToken() -> String? {
    func sha256(_ data: Data) -> Data? {
      guard let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH)) else { return nil }
      CC_SHA256((data as NSData).bytes, CC_LONG(data.count), res.mutableBytes.assumingMemoryBound(to: UInt8.self))
      return res as Data
    }
    let dateString = Date().toString(format: .shortdate)
    let tokenString = GlobalVars.apiKey + dateString
    
    guard
      let data = tokenString.data(using: String.Encoding.utf8),
      let shaData = sha256(data)
      else {
        return nil
    }
    let rc = shaData.base64EncodedString(options: [])
    return rc
  }
  
  private func getBaseHeader() -> Dictionary<String, String> {
    let apiToken = getApiToken()
    return ["Accept-Encoding": "gzip",
            "Content-Type": "application/json",
            "Accept": "application/json",
            "Api-Token": apiToken == nil ? "" : apiToken!]
  }
  
  var memeberHeader: [String: String] {
    get {
      var header = getBaseHeader()
      if UserManager.shared.currentUser != nil {
        header["Authorization"] = "\(UserManager.shared.currentUser!.identifier)| token=\((UserManager.shared.currentUser!.token))"
      }
      return header
    }
  }
  
  var header: [String: String] {
    get {
      return getBaseHeader()
    }
  }
  
  var uploadHeader: [String: String] {
    get {
      var header = getBaseHeader()
      header.removeValue(forKey: "Content-Type")
      return header
    }
  }
  
  typealias APIJSONResponse = ((JSON) -> Void)
  typealias APIErrorResponse = ((String, Int) -> Void)
  
  func requestAPI(_ urlString: String,
                  method: HTTPMethod,
                  body: Parameters? = nil,
                  header: [String: String]? = nil,
                  encoding: ParameterEncoding = JSONEncoding.default,
                  successCallback: @escaping APIJSONResponse,
                  errorCallback: @escaping APIErrorResponse) {
    
    let theHeader = (header == nil ? self.header : header)
    Alamofire.request(urlString, method: method, parameters: body, encoding: encoding, headers: theHeader).responseJSON { response in
      
      let statusCode = response.response?.statusCode
      if statusCode == 401 {
        loggingPrint("API Failure: message: 用户未登录, code: -1")
        errorCallback("用户未登录", -1)
        UserManager.shared.currentUser = nil
//        NotificationCenter.default.post(name: .backToLogin, object: nil)
        
      } else {
        switch response.result {
        case .success(let value):
          
          let json = JSON(value)
          let success = json["success"].boolValue
          guard success else {
            // server error
            let message = json["message"].stringValue
            let code = json["code"].intValue

            loggingPrint("API Failure: message: \(message), code: \(code)")
            errorCallback(message, code)
            return
          }
          
          // success
//          loggingPrint(json)
          successCallback(json)
          
        case .failure(let error):
          loggingPrint("API Failure: message: \(error.localizedDescription), code: -2")
          errorCallback("服务器错误，请检查网络连接", -2)
        }
      }
    }
  }
  
  func upload(_ urlString: String,
              parameters: Parameters? = nil,
              uploadData: UploadData?,
              successCallback: @escaping APIJSONResponse,
              errorCallback: @escaping APIErrorResponse) {
    
    Alamofire.upload(multipartFormData: { multipartFormData in
      if let uData = uploadData {
        multipartFormData.append(uData.data, withName: uData.key, fileName: uData.fileName, mimeType: uData.mimeType.rawValue)
      }
      if let param = parameters {
        for (key, value) in param {
          multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
        }
      }
    }, to: urlString, method: .post, headers: API.shared.uploadHeader, encodingCompletion: { encodingResult in
      switch encodingResult {
      case .success(let upload, _, _):
        upload.responseJSON { response in
          switch response.result {
          case .success(let value):
            
            let json = JSON(value)
            
            let success = json["success"].boolValue
            guard success else {
              // server error
              let message = json["message"].stringValue
              let code = json["code"].intValue
              
              loggingPrint("API Failure: message: \(message), code: \(code)")
              errorCallback(message, code)
              return
            }
            loggingPrint(json)
            successCallback(json)
          case .failure(let error):
            loggingPrint("API Failure: message: \(error.localizedDescription), code: -2")
            errorCallback("服务器错误，请检查网络连接", -2)
          }
        }
      case .failure(let error):
        loggingPrint("API Failure: message: \(error.localizedDescription), code: -2")
        errorCallback("服务器错误，请检查网络连接", -2)
      }
    })
  }
    
    func requestBodyStrWithDic(_ info: Dictionary<String, Any>) ->String! {
        var result:String = ""
        for (key,value) in info {
            result = result + "&\(key)=\(value)"
        }
        result.remove(at: result.startIndex)
        return result
    }
    
    static func requsetPost(_ urlString:String!,
                            parameters: Dictionary<String,Any>?,
                            success:@escaping(JSON)->Void,
                            fail:@escaping(Error) -> Void) {
        Alamofire.request(urlString, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseJSON { (result) in
            switch (result.result) {
            case .success(let value):
                let jsonResult = JSON(value)
                success(jsonResult)
            case .failure(let error):
                fail(error)
            }
        }
        
    }
}
